package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	mqtt "github.com/mochi-mqtt/server/v2"
	"github.com/mochi-mqtt/server/v2/listeners"
	"github.com/mochi-mqtt/server/v2/packets"
)

type ConnectHook struct {
	server *mqtt.Server // 引用服务器实例
	mqtt.HookBase
}

func (h *ConnectHook) ID() string {
	return "connack-hook"
}

func (h *ConnectHook) Provides(b byte) bool {
	// 声明这个钩子提供 OnSessionEstablished 事件
	return bytes.Contains([]byte{
		mqtt.OnConnect,
		mqtt.OnSessionEstablished,
	}, []byte{b})
}

// OnConnect 钩子：返回 (packets.Packet, error)
func (h *ConnectHook) OnConnect(cl *mqtt.Client, pk packets.Packet) error {
	fmt.Println("Client connected: %s", cl.ID)

	props := pk.Properties
	fmt.Printf("Connect Properties.AssignedClientID: %v\n", props.AssignedClientID)
	fmt.Printf("Connect Properties.AuthenticationData: %v\n", props.AuthenticationData)
	fmt.Printf("Connect Properties.AuthenticationMethod: %v\n", props.AuthenticationMethod)
	fmt.Printf("Connect Properties.ContentType: %v\n", props.ContentType)
	fmt.Printf("Connect Properties.CorrelationData: %v\n", props.CorrelationData)
	fmt.Printf("Connect Properties.MaximumPacketSize: %v\n", props.MaximumPacketSize)
	fmt.Printf("Connect Properties.MaximumQos: %v\n", props.MaximumQos)
	fmt.Printf("Connect Properties.MaximumQosFlag: %v\n", props.MaximumQosFlag)
	fmt.Printf("Connect Properties.MessageExpiryInterval: %v\n", props.MessageExpiryInterval)
	fmt.Printf("Connect Properties.PayloadFormat: %v\n", props.PayloadFormat)
	fmt.Printf("Connect Properties.PayloadFormatFlag: %v\n", props.PayloadFormatFlag)
	fmt.Printf("Connect Properties.ReasonString: %v\n", props.ReasonString)
	fmt.Printf("Connect Properties.ReceiveMaximum: %v\n", props.ReceiveMaximum)
	fmt.Printf("Connect Properties.RequestProblemInfo: %v\n", props.RequestProblemInfo)
	fmt.Printf("Connect Properties.RequestProblemInfoFlag: %v\n", props.RequestProblemInfoFlag)
	fmt.Printf("Connect Properties.RequestResponseInfo: %v\n", props.RequestResponseInfo)
	fmt.Printf("Connect Properties.ResponseInfo: %v\n", props.ResponseInfo)
	fmt.Printf("Connect Properties.ResponseTopic: %v\n", props.ResponseTopic)
	fmt.Printf("Connect Properties.RetainAvailable: %v\n", props.RetainAvailable)
	fmt.Printf("Connect Properties.RetainAvailableFlag: %v\n", props.RetainAvailableFlag)
	fmt.Printf("Connect Properties.ServerKeepAlive: %v\n", props.ServerKeepAlive)
	fmt.Printf("Connect Properties.ServerKeepAliveFlag: %v\n", props.ServerKeepAliveFlag)
	fmt.Printf("Connect Properties.ServerReference: %v\n", props.ServerReference)
	fmt.Printf("Connect Properties.SessionExpiryInterval: %v\n", props.SessionExpiryInterval)
	fmt.Printf("Connect Properties.SessionExpiryIntervalFlag: %v\n", props.SessionExpiryIntervalFlag)
	fmt.Printf("Connect Properties.SharedSubAvailable: %v\n", props.SharedSubAvailable)
	fmt.Printf("Connect Properties.SharedSubAvailableFlag: %v\n", props.SharedSubAvailableFlag)
	fmt.Printf("Connect Properties.SubIDAvailable: %v\n", props.SubIDAvailable)
	fmt.Printf("Connect Properties.SubIDAvailableFlag: %v\n", props.SubIDAvailableFlag)
	fmt.Printf("Connect Properties.SubscriptionIdentifier: %v\n", props.SubscriptionIdentifier)
	fmt.Printf("Connect Properties.TopicAlias: %v\n", props.TopicAlias)
	fmt.Printf("Connect Properties.TopicAliasFlag: %v\n", props.TopicAliasFlag)
	fmt.Printf("Connect Properties.TopicAliasMaximum: %v\n", props.TopicAliasMaximum)
	if props.User != nil {
		for _, up := range props.User {
			fmt.Println("Connect Properties.UserProperty:", up.Key, "=", up.Val)
		}
	}
	fmt.Printf("Connect Properties.WildcardSubAvailable: %v\n", props.WildcardSubAvailable)
	fmt.Printf("Connect Properties.WildcardSubAvailableFlag: %v\n", props.WildcardSubAvailableFlag)
	fmt.Printf("Connect Properties.WillDelayInterval: %v\n", props.WillDelayInterval)
	// 🔍 打印确认
	fmt.Printf("✅ Final UserProperties: %+v\n", cl.Properties.Props.User)
	// // ✅ 设置 userProperties
	userProps := &packets.Properties{
		User: []packets.UserProperty{},
	}
	connectProps := pk.Properties
	// 将所有 CONNECT properties 转换为 User properties
	if connectProps.AssignedClientID != "" {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "assignedClientID",
			Val: connectProps.AssignedClientID,
		})
	}

	if connectProps.AuthenticationMethod != "" {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "authenticationMethod",
			Val: connectProps.AuthenticationMethod,
		})
	}

	if connectProps.AuthenticationData != nil {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "authenticationData",
			Val: string(connectProps.AuthenticationData),
		})
	}

	if connectProps.ContentType != "" {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "contentType",
			Val: connectProps.ContentType,
		})
	}

	if connectProps.CorrelationData != nil {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "correlationData",
			Val: string(connectProps.CorrelationData),
		})
	}

	if connectProps.MaximumPacketSize != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "maximumPacketSize",
			Val: fmt.Sprintf("%d", connectProps.MaximumPacketSize),
		})
	}

	if connectProps.MaximumQosFlag {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "maximumQos",
			Val: fmt.Sprintf("%d", connectProps.MaximumQos),
		})
	}

	if connectProps.MessageExpiryInterval != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "messageExpiryInterval",
			Val: fmt.Sprintf("%d", connectProps.MessageExpiryInterval),
		})
	}

	if connectProps.PayloadFormatFlag {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "payloadFormat",
			Val: fmt.Sprintf("%d", connectProps.PayloadFormat),
		})
	}

	if connectProps.ReceiveMaximum != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "receiveMaximum",
			Val: fmt.Sprintf("%d", connectProps.ReceiveMaximum),
		})
	}

	if connectProps.RequestProblemInfoFlag {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "requestProblemInfo",
			Val: fmt.Sprintf("%d", connectProps.RequestProblemInfo),
		})
	}

	if connectProps.RequestResponseInfo != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "requestResponseInfo",
			Val: "1",
		})
	}

	if connectProps.ResponseTopic != "" {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "responseTopic",
			Val: connectProps.ResponseTopic,
		})
	}

	if connectProps.ServerKeepAliveFlag {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "serverKeepAlive",
			Val: fmt.Sprintf("%d", connectProps.ServerKeepAlive),
		})
	}

	if connectProps.ServerReference != "" {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "serverReference",
			Val: connectProps.ServerReference,
		})
	}

	if connectProps.SessionExpiryIntervalFlag {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "sessionExpiryInterval",
			Val: fmt.Sprintf("%d", connectProps.SessionExpiryInterval),
		})
	}

	if connectProps.TopicAliasMaximum != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "topicAliasMaximum",
			Val: fmt.Sprintf("%d", connectProps.TopicAliasMaximum),
		})
	}

	if connectProps.WillDelayInterval != 0 {
		userProps.User = append(userProps.User, packets.UserProperty{
			Key: "willDelayInterval",
			Val: fmt.Sprintf("%d", connectProps.WillDelayInterval),
		})
	}

	// 添加原始的 User properties（如果有的话）
	if connectProps.User != nil {
		for _, prop := range connectProps.User {
			userProps.User = append(userProps.User, packets.UserProperty{
				Key: "User_" + prop.Key,
				Val: prop.Val,
			})
		}
	}

	cl.Properties.Props = *userProps
	// // ✅ 发送 CONNACK（支持 MQTT 5.0 Properties）
	// // 参数：client, sessionPresent, reasonCode, properties
	err := h.server.SendConnack(
		cl,                  // 客户端
		packets.CodeSuccess, // ReasonCode: 0x00 表示成功
		false,               // SessionPresent (true if session exists)
		userProps,           // 包含 userProperties
	)
	if err != nil {
		return err // 返回错误会中断连接
	}

	// 返回 nil error 表示继续连接流程
	return packets.ErrRejectPacket
}

func (h *ConnectHook) OnSessionEstablished(cl *mqtt.Client, pk packets.Packet) {
	// ✅ 在这里设置 CONNACK 属性
	// cl.Properties.Props.ReasonString = "Connection accepted"
	// if cl.Properties.Props.User == nil {
	// 	cl.Properties.Props.User = make([]packets.UserProperty, 0)
	// }
	// cl.Properties.Props.User = append(cl.Properties.Props.User, packets.UserProperty{
	// 	Key: "server",
	// 	Val: "mochi-mqtt-proxy",
	// })

	fmt.Println("Session established for client:", cl.ID)
}

// AllowAllACLHook 允许所有用户访问所有主题（测试用）
type AllowAllACLHook struct {
	mqtt.HookBase
}

func (h *AllowAllACLHook) ID() string {
	return "allow-all-acl"
}

func (h *AllowAllACLHook) Provides(b byte) bool {
	return bytes.Contains([]byte{
		mqtt.OnACLCheck,
	}, []byte{b})
}

// OnACLCheck 控制客户端是否可以订阅或发布某个主题
func (h *AllowAllACLHook) OnACLCheck(cl *mqtt.Client, topic string, write bool) bool {
	// write == true 表示是发布（PUBLISH）
	// write == false 表示是订阅（SUBSCRIBE）

	// ✅ 这里返回 true 表示允许
	// 你可以在这里添加更细粒度的控制，比如：
	// if cl.Username == "admin" { return true }
	// if strings.HasPrefix(topic, "public/") { return true }

	fmt.Printf("[ACL] 用户=%s 主题=%s 操作=%s ✅ 允许\n",
		string(cl.Properties.Username),
		topic,
		map[bool]string{true: "发布", false: "订阅"}[write])
	return true
}

type UsernamePasswordAuthHook struct {
	mqtt.HookBase
	ValidUsers map[string]string // username -> password
}

func NewUsernamePasswordAuthHook(users map[string]string) *UsernamePasswordAuthHook {
	return &UsernamePasswordAuthHook{
		ValidUsers: users,
	}
}

func (h *UsernamePasswordAuthHook) Provides(b byte) bool {
	return bytes.Contains([]byte{
		mqtt.OnConnectAuthenticate,
	}, []byte{b})
}

// OnConnectAuthenticate 实现 v2 版本的认证方法
func (h *UsernamePasswordAuthHook) OnConnectAuthenticate(cl *mqtt.Client, pk packets.Packet) bool {
	if !pk.Connect.UsernameFlag || !pk.Connect.PasswordFlag {
		fmt.Println("客户端未提供用户名或密码")
		return false
	}

	username := string(pk.Connect.Username)
	password := string(pk.Connect.Password)
	fmt.Println(username)
	fmt.Println(password)
	expectedPass, exists := h.ValidUsers[username]
	if !exists {
		fmt.Printf("用户不存在: %s\n", username)
		return false
	}

	if expectedPass != password {
		fmt.Printf("密码错误: %s\n", username)
		return false
	}

	fmt.Printf("登录成功: 用户名=%s\n", username)

	// ✅ 打印 Connect Properties

	return true
}

func main() {
	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-sigs
		done <- true
	}()

	tlsConfig := createTLSConfig()

	server := mqtt.New(&mqtt.Options{
		InlineClient: true,
	})
	validUsers := map[string]string{
		"user1": "pass1",
		"admin": "password123",
	}
	_ = server.AddHook(NewUsernamePasswordAuthHook(validUsers), nil)
	_ = server.AddHook(newMessageLogger(server), &ExampleHookOptions{
		Server: server,
	})
	_ = server.AddHook(&AllowAllACLHook{}, nil)
	_ = server.AddHook(&ConnectHook{server: server}, nil)

	tcp := listeners.NewTCP(listeners.Config{
		ID:        "t1",
		Address:   "0.0.0.0:1883",
		TLSConfig: tlsConfig,
	})
	err := server.AddListener(tcp)
	if err != nil {
		log.Fatal(err)
	}

	ws := listeners.NewWebsocket(listeners.Config{
		ID:        "ws1",
		Address:   "0.0.0.0:1882",
		TLSConfig: tlsConfig,
	})
	err = server.AddListener(ws)
	if err != nil {
		log.Fatal(err)
	}

	stats := listeners.NewHTTPStats(
		listeners.Config{
			ID:        "stats",
			Address:   "0.0.0.0:8080",
			TLSConfig: tlsConfig,
		}, server.Info,
	)
	err = server.AddListener(stats)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		err := server.Serve()
		if err != nil {
			log.Fatal(err)
		}
	}()

	<-done
	server.Log.Warn("caught signal, stopping...")
	_ = server.Close()
	server.Log.Info("main.go finished")
}

func createTLSConfig() *tls.Config {
	// 加载服务端自己的证书和私钥
	cert, err := tls.LoadX509KeyPair("./cert/server.crt", "./cert/server.key")
	if err != nil {
		log.Fatalf("server: loadkeys: %s", err)
	}

	// 加载客户端 CA 证书
	clientCA, err := os.ReadFile("./cert/ca.crt")
	if err != nil {
		log.Fatalf("server: read client ca: %s", err)
	}

	// 创建一个证书池，并将客户端 CA 添加进去
	clientCAPool := x509.NewCertPool()
	ok := clientCAPool.AppendCertsFromPEM(clientCA)
	if !ok {
		log.Fatalf("failed to parse client CA certificate")
	}

	// 构建 TLS 配置
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAndVerifyClientCert, // 强制要求客户端提供证书并验证
		ClientCAs:    clientCAPool,                   // 信任的客户端 CA
		MinVersion:   tls.VersionTLS12,
	}
	return tlsConfig
}

type ExampleHookOptions struct {
	Server *mqtt.Server
}

type messageLogger struct {
	mqtt.HookBase
	server *mqtt.Server
}

// 修改 messageLogger 的构造
func newMessageLogger(server *mqtt.Server) *messageLogger {
	return &messageLogger{
		server: server,
	}
}

// 实现 Serve 方法，这里我们只关心 OnMsgReceived 事件
func (h *messageLogger) ID() string {
	return "message-logger"
}

func (h *messageLogger) Provides(b byte) bool {
	return bytes.Contains([]byte{
		mqtt.OnPublish,
	}, []byte{b})
}

func (h *messageLogger) OnPublish(cl *mqtt.Client, pk packets.Packet) (packets.Packet, error) {
	fmt.Println("received from client", "client", cl.ID, "payload", string(pk.Payload), pk.ReasonCode)
	props := pk.Properties
	fmt.Printf("Publish Properties.AssignedClientID: %v\n", props.AssignedClientID)
	fmt.Printf("Publish Properties.AuthenticationData: %v\n", props.AuthenticationData)
	fmt.Printf("Publish Properties.AuthenticationMethod: %v\n", props.AuthenticationMethod)
	fmt.Printf("Publish Properties.ContentType: %v\n", props.ContentType)
	fmt.Printf("Publish Properties.CorrelationData: %v\n", props.CorrelationData)
	fmt.Printf("Publish Properties.MaximumPacketSize: %v\n", props.MaximumPacketSize)
	fmt.Printf("Publish Properties.MaximumQos: %v\n", props.MaximumQos)
	fmt.Printf("Publish Properties.MaximumQosFlag: %v\n", props.MaximumQosFlag)
	fmt.Printf("Publish Properties.MessageExpiryInterval: %v\n", props.MessageExpiryInterval)
	fmt.Printf("Publish Properties.PayloadFormat: %v\n", props.PayloadFormat)
	fmt.Printf("Publish Properties.PayloadFormatFlag: %v\n", props.PayloadFormatFlag)
	fmt.Printf("Publish Properties.ReasonString: %v\n", props.ReasonString)
	fmt.Printf("Publish Properties.ReceiveMaximum: %v\n", props.ReceiveMaximum)
	fmt.Printf("Publish Properties.RequestProblemInfo: %v\n", props.RequestProblemInfo)
	fmt.Printf("Publish Properties.RequestProblemInfoFlag: %v\n", props.RequestProblemInfoFlag)
	fmt.Printf("Publish Properties.RequestResponseInfo: %v\n", props.RequestResponseInfo)
	fmt.Printf("Publish Properties.ResponseInfo: %v\n", props.ResponseInfo)
	fmt.Printf("Publish Properties.ResponseTopic: %v\n", props.ResponseTopic)
	fmt.Printf("Publish Properties.RetainAvailable: %v\n", props.RetainAvailable)
	fmt.Printf("Publish Properties.RetainAvailableFlag: %v\n", props.RetainAvailableFlag)
	fmt.Printf("Publish Properties.ServerKeepAlive: %v\n", props.ServerKeepAlive)
	fmt.Printf("Publish Properties.ServerKeepAliveFlag: %v\n", props.ServerKeepAliveFlag)
	fmt.Printf("Publish Properties.ServerReference: %v\n", props.ServerReference)
	fmt.Printf("Publish Properties.SessionExpiryInterval: %v\n", props.SessionExpiryInterval)
	fmt.Printf("Publish Properties.SessionExpiryIntervalFlag: %v\n", props.SessionExpiryIntervalFlag)
	fmt.Printf("Publish Properties.SharedSubAvailable: %v\n", props.SharedSubAvailable)
	fmt.Printf("Publish Properties.SharedSubAvailableFlag: %v\n", props.SharedSubAvailableFlag)
	fmt.Printf("Publish Properties.SubIDAvailable: %v\n", props.SubIDAvailable)
	fmt.Printf("Publish Properties.SubIDAvailableFlag: %v\n", props.SubIDAvailableFlag)
	fmt.Printf("Publish Properties.SubscriptionIdentifier: %v\n", props.SubscriptionIdentifier)
	fmt.Printf("Publish Properties.TopicAlias: %v\n", props.TopicAlias)
	fmt.Printf("Publish Properties.TopicAliasFlag: %v\n", props.TopicAliasFlag)
	fmt.Printf("Publish Properties.TopicAliasMaximum: %v\n", props.TopicAliasMaximum)
	if props.User != nil {
		for _, up := range props.User {
			fmt.Println("Publish Properties.UserProperty:", up.Key, "=", up.Val)
		}
	}
	fmt.Printf("Publish Properties.WildcardSubAvailable: %v\n", props.WildcardSubAvailable)
	fmt.Printf("Publish Properties.WildcardSubAvailableFlag: %v\n", props.WildcardSubAvailableFlag)
	fmt.Printf("Publish Properties.WillDelayInterval: %v\n", props.WillDelayInterval)
	pkx := pk.Copy(true)

	pkx.Properties.User = append(pkx.Properties.User, packets.UserProperty{
		Key: "server",
		Val: "mochi-mqtt-proxy",
	})
	pkx.Properties.User = append(pkx.Properties.User, packets.UserProperty{
		Key: "timestamp",
		Val: fmt.Sprintf("%d", time.Now().Unix()),
	})

	fmt.Println("received modified packet from client", "client", cl.ID, "payload", string(pkx.Payload), pkx.ReasonCode)

	return pkx, nil
}
